package com.caishi.lkx.user.api;

import com.alibaba.fastjson.JSONObject;
import com.caishi.lkx.common.BaseApi;
import com.caishi.lkx.common.model.PaperQuestionLogDto;
import com.caishi.lkx.common.role.Role;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.exam.mapper.KaoyanQuestionMapper;
import com.caishi.lkx.exam.model.ExamModel;
import com.caishi.lkx.exam.model.KaoyaoQuestionModel;
import com.caishi.lkx.exam.model.PaperModel;
import com.caishi.lkx.exam.service.IPaperService;
import com.caishi.lkx.user.ienum.type.UserLogSourceType;
import com.zzw.common.Wrappers;
import com.zzw.common.annotation.ApiTimeRecord;

import com.caishi.lkx.user.userlog.NormalLogRecordsService;
import com.caishi.lkx.user.userlog.model.Count;
import com.caishi.lkx.user.userlog.model.IndustryLogRecords;
import com.caishi.lkx.user.userlog.model.UserLogType;
import com.caishi.lkx.user.userlog.vo.ClientCountVo;
import com.caishi.lkx.user.userlog.vo.ClientTableRow;
import com.caishi.lkx.user.userlog.vo.TableRow;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Tag(name = "用户日志相关接口")
@RestController
@RequestMapping("/log-records")
@Slf4j
public class LogRecordsApi extends BaseApi {

    @Resource
    private NormalLogRecordsService normalLogRecordsService;

//    @Resource
//    private IGroupLogRuleService groupLogRuleService;

    private final String KAOYAN_REGULATION = "yjs";

    @Resource
    private IPaperService paperService;
//    @Value("${kaoyan.log-url}")
//    private String kaoyanLogUrl;
    @Resource
    private KaoyanQuestionMapper kaoyanQuestionMapper;

    @Getter
    @Setter
    public static class Image {

        private JSONObject image;
    }


//    @PostMapping("/record")
//    @Roles
//    @Operation(summary = "用户操作埋点")
//    public void addRecords(@RequestParam UserLogType logType,
//                           @RequestParam UserLogSourceType sourceType,
//                           String industryId,
//                           String examId,
//                           @RequestParam(defaultValue = "1") Double count,
//                           @ModelAttribute Image imageModel
//    ) {
//        var userGroupId = userContext.currentGroupId();
//        var localGroupId = userContext.currentLocalGroupId();
//        var userId = userContext.currentUserId();
//        var ip = userContext.currentIp();
//        var image = imageModel.getImage();
//        //根据传来的ids存放对应数量的记录
//        if (image.containsKey("ids")) {
//            String ids = image.getString("ids");
//            if (StringUtil.isNotBlank(ids)) {
//                count = (double) ids.split(",").length;
//            }
//        }
//        Double finalCount = count;
//        Runnable runnable = () -> {
//            List<Long> groupIds = new LinkedList<>();
//            var normal = !logType.isInd();
//            Consumer<Long> work = id -> {
//                var c = id > 0 ? groupLogRuleService.getMachiningCount(id, logType, sourceType, industryId, userId, ip, finalCount) : finalCount;
//                if (normal) normalLogRecordsService.addTodayData(logType, id, sourceType, examId, c, finalCount);
//                else
//                    normalLogRecordsService.addIndustryTodayData(logType, id, sourceType, industryId, examId, c, finalCount);
//                groupIds.add(id);
//            };
//            if (userGroupId != null) {
//                image.put("userGroupId", userGroupId);
//                work.accept(userGroupId);
//            } else if (localGroupId != null) {
//                image.put("localGroupId", userGroupId);
//                work.accept(localGroupId);
//            }
//            work.accept(-1L);
//            if (userGroupId == null && localGroupId == null) {
//                work.accept(0L);
//            }
//            normalLogRecordsService.saveRecordImage(logType, industryId, examId, groupIds, userId, ip, image);
//        };
//        SysThreadPool.execute(() -> {
//            this.sendKaoyanPlatform(logType, image, ip, userId, userGroupId, localGroupId);
//        });
//        runnable.run();
//    }

    private void sendKaoyanPlatform(UserLogType logType, JSONObject image, String ip, String userId, Long userGroupId, Long localGroupId) {
        PaperQuestionLogDto paperQuestionLogDto = null;
        if (logType.equals(UserLogType.paperUseLog)) {
            Object paperId = image.get("paperId");
            //获取试卷判断考研平台是否配置
            if (null != paperId) {
                String id = String.valueOf(paperId);
                PaperModel paperModel = paperService.selectFirst(Wrappers.<PaperModel>lambdaQuery().eq(PaperModel::getId, id).select(PaperModel::getKaoyanCategoryId));
                //考研平台存在就添加统计
                if (null != paperModel.getKaoyanCategoryId() && 0 != paperModel.getKaoyanCategoryId()) {
                    paperQuestionLogDto = PaperQuestionLogDto.builder().ip(ip).count(1D).image(image).userId(userId).groupId(userGroupId).type(2).localGroupId(localGroupId).build();
                }
            }
        }
        if (logType.equals(UserLogType.questionUseLog)) {
            Object questionId = image.get("id");
            if (null != questionId) {
                String id = String.valueOf(questionId);
                Long c = kaoyanQuestionMapper.selectCount(Wrappers.<KaoyaoQuestionModel>lambdaQuery().eq(KaoyaoQuestionModel::getId, id));
                if (c > 0) {
                    paperQuestionLogDto = PaperQuestionLogDto.builder().ip(ip).count(1D).image(image).userId(userId).groupId(userGroupId).type(1).localGroupId(localGroupId).build();
                }
            }

        }
//        if (null != paperQuestionLogDto) {
//            log.info("发送到考研平台: {}", JSON.toJSONString(paperQuestionLogDto));
//            HttpRequest post = HttpUtil.createPost(kaoyanLogUrl);
//            post.body(JSON.toJSONString(paperQuestionLogDto), MediaType.APPLICATION_JSON.toString());
//            post.execute();
//        }
    }

    @GetMapping("/record-list")
    @Operation(summary = "机构统计总览")
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, ClientTableRow> groupRecordList(
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "all") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        Map<LocalDate, ClientTableRow> res = new LinkedHashMap<>();
        groupRecordList(userContext.currentGroupId(), sourceTypes, start, end, groupBy, setExam)
                .forEach((k, v) -> res.put(k, v.toClient()));
        return res;
    }


    @GetMapping("/record-detail-list")
    @Operation(summary = "机构记录详情列表")
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, ClientCountVo> groupRecordDetailList(
            @RequestParam UserLogType type,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "month") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        return countVo2Client(groupRecordDetailList(userContext.currentGroupId(), type, sourceTypes, start, end, groupBy, setExam));
    }

    @GetMapping("/industry-record-detail-list")
    @Operation(summary = "机构记录详情列表")
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, Map<String, ClientCountVo>> groupIndustryRecordDetailList(
            @RequestParam List<String> industry,
            @RequestParam UserLogType type,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "month") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        var data = groupIndustryRecordDetailList(userContext.currentGroupId(), industry, type, sourceTypes, start, end, groupBy, setExam);
        Map<LocalDate, Map<String, ClientCountVo>> res = new LinkedHashMap<>();
        data.forEach((t, m) -> {
            Map<String, ClientCountVo> line = new HashMap<>();
            m.forEach((id, c) -> line.put(id, c.toClient()));
            res.put(t, line);
        });
        return res;
    }


    @GetMapping("/last-week-access")
    @Roles(value = Role.admin)
    public Map<String, Object> getLastWeek(
            @RequestParam(defaultValue = "-1") Long groupId,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "day") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        Map<LocalDate, TableRow> localDateTableRowMap = normalLogRecordsService.groupRecordList(groupId, List.of(sourceTypes), start, end(end, groupBy), groupBy, setExam);
        HashMap<String, Object> map = new HashMap<>();
        List<Double> list = localDateTableRowMap.values().stream().map(c -> c.getAccessLog().getAll().getTodayCount())
                .collect(Collectors.toList());
        map.put("x", localDateTableRowMap.keySet());
        map.put("value", list);
        return map;
    }

    @GetMapping("/last-day-access")
    @Roles(value = Role.admin)
    public Map<String, Object> getLastDay(
            @RequestParam(defaultValue = "-1") Long groupId,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "day") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        Map<LocalDate, TableRow> localDateTableRowMap = normalLogRecordsService.groupRecordList(groupId, List.of(sourceTypes), start, end(end, groupBy), groupBy, setExam);
        HashMap<String, Object> map = new HashMap<>();
        Count accessLog = localDateTableRowMap.get(end).getAccessLog();
        List list = new ArrayList();
        list.add(accessLog.getPcZhiYe().getTodayCount());
        list.add(accessLog.getPcGjtk().getTodayCount());
        list.add(accessLog.getAppZhiYe().getTodayCount());
        list.add(accessLog.getAppGjtk().getTodayCount());
        // list.add(accessLog.getWpZhiYeApp().getTodayCount());
        list.add(accessLog.getWpZSZhiYe().getTodayCount());
        list.add(accessLog.getWpZSGjtk().getTodayCount());
        map.put("x", Arrays.stream(sourceTypes).map(c -> c.getDesc()));
        map.put("value", list);
        return map;
    }

    @GetMapping("/admin-record-list")
    @Roles(value = Role.admin)
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, TableRow> groupRecordList(
            @RequestParam Long groupId,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "day") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        if (start == null && end == null) {
            return new HashMap<>() {{
                put(LocalDate.of(1, 1, 1), normalLogRecordsService.groupRecordAll(groupId, List.of(sourceTypes), setExam));
            }};
        }
        return normalLogRecordsService.groupRecordList(groupId, List.of(sourceTypes), start, end(end, groupBy), groupBy, setExam);
    }

    @GetMapping("/admin-record-detail-list")
    @Roles(value = Role.admin)
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, Count> groupRecordDetailList(
            @RequestParam Long groupId,
            @RequestParam UserLogType type,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "month") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam) {
        if (start == null && end == null) {
            return new HashMap<>() {{
                put(LocalDate.of(1, 1, 1), normalLogRecordsService.groupOneLogTypeRecordAll(groupId, type, List.of(sourceTypes), setExam));
            }};
        } else {
            return normalLogRecordsService.groupRecordList(groupId, type, List.of(sourceTypes), start, end, groupBy, setExam);
        }
    }

    @GetMapping("/admin-industry-record-detail-list")
    @Roles(value = Role.admin)
    @ApiTimeRecord(gt = 3000)
    public Map<LocalDate, Map<String, Count>> groupIndustryRecordDetailList(
            @RequestParam Long groupId,
            @RequestParam List<String> industry,
            @RequestParam UserLogType type,
            @RequestParam UserLogSourceType[] sourceTypes,
            @RequestParam(required = false) LocalDate start,
            @RequestParam(required = false) LocalDate end,
            @RequestParam(defaultValue = "month") NormalLogRecordsService.DataGroupBy groupBy, boolean setExam
    ) {
        // 1 全部行业 2 职业题库行业 3自定义行业（高教行业）
        var industryType = industry.isEmpty() ? 1 : ExamModel.ZY.equals(industry.get(0)) ? 2 : 3;
        List<String> industryIds = new LinkedList<>();
        if (industryType == 3) {
            industryIds.addAll(industry);
        } else {
            industryIds.addAll(normalLogRecordsService.allIndustry(groupId));
            if (industryType == 1) {
                industryIds.add(ExamModel.GJTK);
            }
        }
        Map<String, List<IndustryLogRecords>> industryData = new ConcurrentHashMap<>();
        var times = Collections.singletonList(LocalDate.of(1, 1, 1));
        if (start == null && end == null) {
            industryIds.parallelStream().forEach(v -> industryData.put(v, Collections.singletonList(normalLogRecordsService.lastNormal(type, groupId, v))));
        } else {
            times = normalLogRecordsService.groupByTimeList(start, end(end, groupBy), groupBy);
            industryIds.parallelStream().forEach(v -> industryData.put(v, normalLogRecordsService.groupIndustryRecordList(groupId, type, v, start, end(end, groupBy))));
        }
        Map<LocalDate, Map<String, Count>> result = new LinkedHashMap<>();
        for (var t : times) result.put(t, new HashMap<>());
        var sourceTypeList = List.of(sourceTypes);
        List<LocalDate> finalTimes = times;
        Map<String, Count> map = new LinkedHashMap<>();
        industryData.forEach((id, rows) -> {
            for (var row : rows) {
                if (row == null) {
                    continue;
                }
                var t = normalLogRecordsService.timeIn(finalTimes, row.getDateT(), groupBy);
                if (t == null) {
                    var oldCount = new Count();
                    oldCount.addAll(normalLogRecordsService.sourcesData(row.getData(), sourceTypeList, setExam), setExam);
                    map.put(id, oldCount);
                    result.put(finalTimes.get(0), map);
                    continue;
                }
                var line = result.get(t);
                var oldCount = line.get(id);
                if (oldCount == null) {
                    oldCount = new Count();
                    line.put(id, oldCount);
                }
                oldCount.addAll(normalLogRecordsService.sourcesData(row.getData(), sourceTypeList, setExam), setExam);
            }
        });
        return result;
    }

    private Map<LocalDate, ClientCountVo> countVo2Client(Map<LocalDate, Count> list) {
        var res = new LinkedHashMap<LocalDate, ClientCountVo>();
        list.forEach((k, v) -> res.put(k, v.toClient()));
        return res;
    }

    private LocalDate end(LocalDate end, NormalLogRecordsService.DataGroupBy groupBy) {
        if (groupBy == NormalLogRecordsService.DataGroupBy.month) {
            return end.plusMonths(1).minusDays(end.getDayOfMonth());
        }
        if (groupBy == NormalLogRecordsService.DataGroupBy.year) {
            return LocalDate.of(end.getYear(), 12, 31);
        }
        return end;
    }
}
